#include "MyApp.hpp"
#include "Tips.hpp"
#include <cstring>
#include <fstream>

MyApp::MyApp()
:Gtk::Application("org.gtk.fiveinarow"),
timer_value(100),
time_sum(0),
game_running(true)
{
    Glib::set_application_name("Five In A Row");
}

Glib::RefPtr<MyApp> MyApp::create()
{
    return Glib::RefPtr<MyApp>(new MyApp());
}

void MyApp::on_startup()
{
    Gtk::Application::on_startup();
}

void MyApp::on_activate(){
    //创建GtkBuilder
    ref_builder=Gtk::Builder::create_from_resource("/FiveInARow/main_window.ui");

    //获取窗口
    ref_builder->get_widget("main_window",main_window);
    main_window->set_icon_name("FiveRow");

    //获取其他构件
    ref_builder->get_widget("main_stack",main_stack);
    ref_builder->get_widget("main_menu",popover);
    ref_builder->get_widget("new_game_screen",new_game_screen);
    ref_builder->get_widget("custom_game_screen",custom_game_screen);
    ref_builder->get_widget("main_screen",main_screen);
    ref_builder->get_widget("clock_label",time_label);
    ref_builder->get_widget("play_pause_label",pause_label);
    ref_builder->get_widget("scrolled",scrolled);
    ref_builder->get_widget("rows_spin_btn",rows_spin_button);
    ref_builder->get_widget("cols_spin_btn",cols_spin_button);
    ref_builder->get_widget("flag_label",winner_label);
    ref_builder->get_widget("play_pause_button",play_pause_btn);
    ref_builder->get_widget("gamemode_combo",gamemode_combo);

    //构建菜单
    menu_builder=Gtk::Builder::create_from_resource("/FiveInARow/appmenu.xml");
    auto object=menu_builder->get_object("app-menu");
    auto gmenu=Glib::RefPtr<Gio::Menu>::cast_dynamic(object);
    popover->bind_model(gmenu);

    //添加菜单及窗口按钮信号
    add_action("quit",sigc::mem_fun(*this,&MyApp::quit));//退出应用
    add_action("introduce",sigc::mem_fun(*this,&MyApp::game_info));//游戏介绍
    add_action("about",sigc::mem_fun(*this,&MyApp::about_game));//关于游戏
    add_action("custom_size",sigc::mem_fun(*this,&MyApp::custom_game));
    add_action("start_custom",sigc::mem_fun(*this,&MyApp::board_custom_size));
    add_action("cancel_custom",sigc::mem_fun(*this,&MyApp::custom_cancel));
    add_action("new_game",sigc::mem_fun(*this,&MyApp::new_game));
    add_action("pause",sigc::mem_fun(*this,&MyApp::pause_game));
    add_action("repeat-size",sigc::mem_fun(*this,&MyApp::repeat_game));
    add_action("small_size",sigc::mem_fun(*this,&MyApp::board_normal_mode));
    add_action("medium_size",sigc::mem_fun(*this,&MyApp::board_fight_mode));
    add_action("large_size",sigc::mem_fun(*this,&MyApp::board_load_file));
    add_action("save_game",sigc::mem_fun(*this,&MyApp::save_game));
    add_action("board_undo",sigc::mem_fun(*this,&MyApp::undo_game));

    //设置提示信息自动换行
    winner_label->set_line_wrap();
    
    //将window添加并显示
    add_window(*main_window);
    main_window->show_all();
}

void MyApp::new_game(){//改变棋盘大小并重开
    main_stack->set_visible_child(*new_game_screen);
    time_sum=0;
    game_running=false;
    mytimer.disconnect();
    pause_label->set_label("暂停");
    if(scrolled->get_child()){//删除之前的棋盘布局
        scrolled->remove();
        chess_board->delete_chesses();
        delete chess_board;
    }
    delete game;
}

void MyApp::start_game(){//开始游戏
    main_stack->set_visible_child(*main_screen);
    time_label->set_label("0:00");
    winner_label->set_label("赢家：");
    play_pause_btn->set_sensitive();
    game_running=true;
    mytimer=Glib::signal_timeout().connect(sigc::mem_fun(*this,&MyApp::on_timeout),timer_value);
}

void MyApp::custom_game(){//自定义棋盘大小
    main_stack->set_visible_child(*custom_game_screen);
}

void MyApp::custom_cancel(){//从自定义页面退回到主页面
    main_stack->set_visible_child(*new_game_screen);
}

void MyApp::pause_game(){//暂停游戏进程
    if(game_running){
        mytimer.disconnect();
        pause_label->set_label("已暂停");
        game_running=false;
        chess_board->set_sensitive(false);
    }else{
        mytimer=Glib::signal_timeout().connect(sigc::mem_fun(*this,&MyApp::on_timeout),timer_value);
        pause_label->set_label("暂停");
        game_running=true;
        chess_board->set_sensitive(true);
    }
}

void MyApp::repeat_game(){//重开游戏
    chess_board->board_clear(curr_rows,curr_cols);          //清空棋盘
    delete game;                                            //重新初始化Game类
    if(game_mode==1){
        game=new gomoku::player_vs_player(curr_rows,curr_cols);
        chess_board->bind_game(game);
    }else{
        game=new gomoku::melee(curr_rows,curr_cols);
        chess_board->bind_game(game);
        chess_board->update_point(curr_rows,curr_cols);
    }
    time_sum=0;                                             //重连计时器并设置提示信息
    time_label->set_label("0:00");
    winner_label->set_label("赢家：");
    chess_board->set_sensitive();
    play_pause_btn->set_sensitive();
    mytimer=Glib::signal_timeout().connect(sigc::mem_fun(*this,&MyApp::on_timeout),timer_value);
    game_running=true;
}

void MyApp::undo_game(){//悔棋
    if(game->undo_operation()){
        chess_board->update_point(curr_rows,curr_cols);
    }else{
        board_msg_box(*main_window,"当前无法悔棋");
    }
}

bool MyApp::on_timeout(){//计时器处理函数
    if(!game_running){//确保游戏暂停时计时器停止
        return false;
    }
    time_sum++;
    if(time_sum%10==0){//更新界面的时间显示
        sprintf(time_string,"%d:%02d",time_sum/600,(time_sum%600)/10);
        time_label->set_label(time_string);
    }
    winner_label->set_label(prompt[game->game_mode()][game->status()]);
    if (game->board.vacant_count()==0 || (winner=game->winner())!=gomoku::player::nobody){
        switch(winner){// 显示胜利者
            case gomoku::player::nobody:
                winner_label->set_label("平局");
                break;
            case gomoku::player::black:
                winner_label->set_label("黑方胜利");
                break;
            case gomoku::player::white:
                winner_label->set_label("白方胜利");
                break;
        }
        chess_board->set_sensitive(false);
        play_pause_btn->set_sensitive(false);
        return false;//停止计时器
    }
    return true;
}

void MyApp::board_normal_mode(){//15x15大小棋盘,普通模式
    game_mode=1;
    curr_rows=15;
    curr_cols=15;
    create_board(curr_rows,curr_cols);
    start_game();
}

void MyApp::board_fight_mode(){//15x15大小棋盘,混战模式
    game_mode=2;
    curr_rows=15;
    curr_cols=15;
    create_board(curr_rows,curr_cols);
    start_game();
}

void MyApp::board_load_file(){//从文件加载棋盘存档
    std::string filename;//保存的文件名
    Gtk::FileChooserDialog open_dialog("打开游戏存档",
                           Gtk::FILE_CHOOSER_ACTION_OPEN);
    open_dialog.set_transient_for(*main_window);
    //添加按钮
    open_dialog.add_button("打开",Gtk::RESPONSE_OK);
    open_dialog.add_button("取消",Gtk::RESPONSE_CANCEL);
    int result=open_dialog.run();
    if(result==Gtk::RESPONSE_OK){
        filename=open_dialog.get_filename();
        try{
            int size,status;
            gomoku::point_status *layout;
            // 载入游戏,载入失败则弹出对话框
            std::ifstream infile;
            infile.open(filename.c_str(),std::ios::in|std::ios::binary);
            infile.read((char*)&game_mode,sizeof(int));
            infile.read((char*)&status,sizeof(int));
            infile.read((char*)&curr_rows,sizeof(int));
            infile.read((char*)&curr_cols,sizeof(int));
            size = curr_rows*curr_cols;
            layout = new gomoku::point_status[size];
            infile.read((char*)layout,size*sizeof(int));
            create_board(status,layout);
            delete [] layout;
        }catch(...){
            board_msg_box(*main_window,"读取失败！");
        }
        start_game();
    }
}

void MyApp::board_custom_size(){//自定义棋盘大小与游戏模式
    curr_rows=rows_spin_button->get_value_as_int();
    curr_cols=cols_spin_button->get_value_as_int();//获取用户设置的行列数
    game_mode=gamemode_combo->get_active_row_number()+1;//ComboBox的id从0开始
    create_board(curr_rows,curr_cols);
    start_game();
}

void MyApp::create_board(int rows,int columns){
    chess_board=new GtkBoard;
    scrolled->add(*chess_board);
    if(game_mode==1){
        game=new gomoku::player_vs_player(rows,columns);//创建游戏后端连接
        chess_board->bind_game(game);
    }else{
        game=new gomoku::melee(rows,columns);
        chess_board->bind_game(game);
    }
    chess_board->create_cheeses(rows,columns);//创建棋盘并连接游戏后端
    chess_board->update_point(rows,columns);
    chess_board->show();
}

void MyApp::create_board(int init_status,gomoku::point_status *init_layout){
    //从已有布局中加载棋盘
    chess_board=new GtkBoard;
    scrolled->add(*chess_board);
    //创建游戏后端连接
    if(game_mode==1){
        game=new gomoku::player_vs_player(curr_rows,curr_cols,init_status,init_layout);
        chess_board->bind_game(game);
    }else{
        game=new gomoku::melee(curr_rows,curr_cols,init_status,init_layout);
        chess_board->bind_game(game);
        //g_print("%d",game->game_mode());
    }
    chess_board->create_cheeses(curr_rows,curr_cols);//创建棋盘并连接游戏后端
    chess_board->update_point(curr_rows,curr_cols);
    chess_board->show();
}

void MyApp::save_game(){//保存游戏进度
    std::string filename;//保存的文件名
    Gtk::FileChooserDialog save_dialog("保存游戏进度",
                           Gtk::FILE_CHOOSER_ACTION_SAVE);//初始化对话框
    save_dialog.set_transient_for(*main_window);
    //添加按钮
    save_dialog.add_button("保存",Gtk::RESPONSE_OK);
    save_dialog.add_button("取消",Gtk::RESPONSE_CANCEL);
    int result=save_dialog.run();
    if(result==Gtk::RESPONSE_OK){
        //将保存的文件名传递至save_game_file函数，以保存游戏状态
        filename=save_dialog.get_filename();
        if(save_game_file(filename.c_str(),*game)){
            board_msg_box(*main_window,"保存成功！");
        }else{
            board_msg_box(*main_window,"保存失败！");
        }
    }
}

bool MyApp::save_game_file(const char *filename, gomoku::game &game){ // 保存游戏,返回保存是否成功
    try{
        std::ofstream outfile;
        outfile.open(filename,std::ios::out|std::ios::binary);
        game.save(outfile);
        outfile.close();
    }catch (...){
        return false;
    }
    return true;
}

void MyApp::about_game(){
    //"关于"对话框
    Gtk::AboutDialog about_dialog;
    about_dialog.set_title("关于五子棋游戏");//对话框标题
    about_dialog.set_transient_for(*main_window);
    about_dialog.set_icon_name("FiveRow");//对话框图标
    about_dialog.set_logo_icon_name("FiveRow");//Logo
    about_dialog.set_version("1.0\nGtkmm version");//版本信息
    about_dialog.run();
}

void MyApp::game_info()
{
    Glib::RefPtr<Gtk::Builder> info_builder;//初始化GtkBuilder
    info_builder=Gtk::Builder::create_from_resource("/FiveInARow/about_dialog.ui");

    if(info_dialog==NULL){
        //如果对话框未初始化，则先执行初始化
        info_builder->get_widget("game_dialog",info_dialog);
        info_builder->get_widget("game_notebook",info_notebook);
        info_builder->get_widget("about_image",about_image);
        about_image->set_from_resource("/FiveInARow/about.png");
        info_notebook->set_current_page(0);
        info_dialog->set_transient_for(*main_window);
        info_dialog->set_icon_name("FiveRow");
    }

    info_dialog->show_all();//运行并显示
    ref_builder.reset();
}
